]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
Improve window handling in overview mode
authorOwen Taylor <otaylor@src.gnome.org>
Thu, 6 Nov 2008 14:00:14 +0000 (14:00 +0000)
committerOwen Taylor <otaylor@src.gnome.org>
Thu, 6 Nov 2008 14:00:14 +0000 (14:00 +0000)
* For small window counts, lay the windows out according to a
  predefined scheme. For larger window counts, continue putting
  the windows along the diagonal as before, but do it a bit better.
* Special case the desktop window and use it as the background
  of the window area.
* Add a little translucency to windows
* Use tweener to animate everything into place
* Clean up - add constants and break things into multiple methods

svn path=/trunk/; revision=32

js/ui/overlay.js

index a58e0bd07879e50c0e4330a0f536bcc82ca21717..85176520ec63563a747ff2325c42a97b81a3d733 100644 (file)
@@ -1,16 +1,40 @@
 /* -*- mode: js2; js2-basic-offset: 4; -*- */
 
-const Shell = imports.gi.Shell;
 const Clutter = imports.gi.Clutter;
+const Meta = imports.gi.Meta;
+const Shell = imports.gi.Shell;
+const Tweener = imports.tweener.tweener;
 
 const Panel = imports.ui.panel;
 
 const OVERLAY_BACKGROUND_COLOR = new Clutter.Color();
 OVERLAY_BACKGROUND_COLOR.from_pixel(0x000000ff);
 
+// Time for initial animation going into overlay mode
+const ANIMATION_TIME = 0.3;
+
+// How much to scale the desktop down by in overlay mode
+const DESKTOP_SCALE = 0.75;
+
+// Windows are slightly translucent in the overlay mode
+const WINDOW_OPACITY = 0.9 * 255;
+
+// Define a layout scheme for small window counts. For larger
+// counts we fall back to an algorithm. We need more schemes here
+// unless we have a really good algorithm.
+//
+// Each triplet is [x_center, y_center, scale] where the scale
+// is relative to the width of the desktop.
+const POSITIONS = {
+    1: [[0.5, 0.5, 0.8]],
+    2: [[0.25, 0.5, 0.4], [0.75, 0.5, 0.4]],
+    3: [[0.2, 0.33, 0.3],  [0.5, 0.67, 0.3],  [0.8, 0.33, 0.3]],
+    4: [[0.25, 0.25, 0.3],   [0.75, 0.25, 0.3], [0.75, 0.75, 0.3], [0.25, 0.75, 0.3]]
+};
+
 function Overlay() {
     this._init();
-}
+};
 
 Overlay.prototype = {
     _init : function() {
@@ -37,50 +61,48 @@ Overlay.prototype = {
        if (!this.visible) {
            this.visible = true;
 
-           // Very simple version of a window overview ... when we show the
-           // overlay, display all the user's windows in the overlay scaled
-           // down.
-           //
-           // We show the window using "clones" of the texture .. separate
-           // actors that mirror the original actors for the window. For
-           // animation purposes, it may be better to actually move the
-           // original actors about instead.
-
            let global = Shell.global_get();
            let windows = global.get_windows();
-       
+           let desktop_window = null;
+
            let screen_width = global.screen_width
            let screen_height = global.screen_height
 
-           let x = screen_width / 4
-           let y = screen_height / 4
-    
            for (let i = 0; i < windows.length; i++)
-               if (!windows[i].is_override_redirect()) {
-                   let clone = new Clutter.CloneTexture({ parent_texture: windows[i].get_texture(),
-                                                          x: x,
-                                                          y: y });
-
-                   // We scale each window down so that it is at most 300x300, but we 
-                   // never want to scale a window up.
-                   let size = clone.width;
-                   if (clone.height > size)
-                       size = clone.height;
-
-                   let scale = 300 / size;
-                   if (scale > 1)
-                       scale = 1;
-                   
-                   clone.set_scale(scale, scale);
-                   this._group.add_actor(clone);
-                   this._window_clones.push(clone);
-
-                   // Windows are overlapped diagonally. If there are too many, they'll
-                   // end up off the screen
-                   x += 50;
-                   y += 50;
-               }
-           
+               if (windows[i].get_window_type() == Meta.WindowType.DESKTOP)
+                   desktop_window = windows[i];
+
+           // The desktop windows are shown on top of a scaled down version of the
+           // desktop. This is positioned at the right side of the screen
+           this._desktop_width = screen_width * DESKTOP_SCALE;
+           this._desktop_height = screen_height * DESKTOP_SCALE;
+           this._desktop_x = screen_width - this._desktop_width - 10;
+           this._desktop_y = Panel.PANEL_HEIGHT + (screen_height - this._desktop_height - Panel.PANEL_HEIGHT) / 2;
+
+           if (desktop_window)
+               this._createDesktopClone(desktop_window);
+
+           // Count the total number of windows so we know what layout scheme to use
+           let n_windows = 0;
+           for (let i = 0; i < windows.length; i++) {
+               let w = windows[i];
+               if (w == desktop_window || w.is_override_redirect())
+                   continue;
+
+               n_windows++;
+           }
+
+           // Now create actors for all the desktop windows
+           let window_index = 0;
+           for (let i = 0; i < windows.length; i++) {
+               let w = windows[i];
+               if (w == desktop_window || w.is_override_redirect())
+                   continue;
+               this._createWindowClone(w, window_index, n_windows);
+
+               window_index++;
+           }
+
            this._group.show();
        }
     },
@@ -96,5 +118,100 @@ Overlay.prototype = {
 
            this._window_clones = [];
        }
+    },
+
+    _createDesktopClone : function(w) {
+       let clone = new Clutter.CloneTexture({ parent_texture: w.get_texture(),
+                                              reactive: true,
+                                              x: 0,
+                                              y: 0 });
+       this._window_clones.push(clone);
+       this._group.add_actor(clone);
+
+       Tweener.addTween(clone,
+                        { x: this._desktop_x,
+                          y: this._desktop_y,
+                          scale_x: DESKTOP_SCALE,
+                          scale_y: DESKTOP_SCALE,
+                          time: ANIMATION_TIME,
+                          transition: "linear"
+                        });
+
+       let me = this;
+       clone.connect("button-press-event",
+                     function() {
+                         me._deactivate();
+                     });
+    },
+
+    _computeWindowPosition : function(window_index, n_windows) {
+       if (n_windows in POSITIONS)
+           return POSITIONS[n_windows][window_index];
+
+       // If we don't have a predefined scheme for this window count, overlap the windows
+       // along the diagonal of the desktop (improve this!)
+       let fraction = Math.sqrt(1/n_windows);
+       let x_center = (fraction / 2) + (1 - fraction) * window_index / (n_windows - 1);
+       let y_center = x_center;
+
+       log(x_center + "," + y_center + "," + fraction);
+       return [x_center, y_center, fraction];
+    },
+
+    _createWindowClone : function(w, window_index, n_windows) {
+       // We show the window using "clones" of the texture .. separate
+       // actors that mirror the original actors for the window. For
+       // animation purposes, it may be better to actually move the
+       // original actors about instead.
+
+       let clone = new Clutter.CloneTexture({ parent_texture: w.get_texture(),
+                                              reactive: true,
+                                              x: w.x,
+                                              y: w.y });
+
+       let [x_center, y_center, fraction] = this._computeWindowPosition(window_index, n_windows);
+
+       let desired_size = this._desktop_width * fraction;
+
+       x_center = this._desktop_x + x_center * this._desktop_width;
+       y_center = this._desktop_y + y_center * this._desktop_height;
+
+       let size = clone.width;
+       if (clone.height > size)
+           size = clone.height;
+
+       // Never scale up
+       let scale = desired_size / size;
+       if (scale > 1)
+           scale = 1;
+
+       this._group.add_actor(clone);
+       this._window_clones.push(clone);
+
+       Tweener.addTween(clone,
+                        { x: x_center - 0.5 * scale * w.width,
+                          y: y_center - 0.5 * scale * w.height,
+                          scale_x: scale,
+                          scale_y: scale,
+                          time: ANIMATION_TIME,
+                          opacity: WINDOW_OPACITY,
+                          transition: "linear"
+                         });
+
+       let me = this;
+       clone.connect("button-press-event",
+                     function() {
+                         me._activateWindow(w);
+                     });
+    },
+
+    _activateWindow : function(w) {
+       this._deactivate();
+       log("Activate "+ w.get_description());
+    },
+
+    _deactivate : function() {
+       let Main = imports.ui.main;
+       Main.hide_overlay();
     }
 };